﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Primitives;
using Serilog;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;

namespace Hicap.QRCodeWarehouse.Core.Identity
{
    public class HicapAuthorizationFilter : IAuthorizationFilter
    {
        private static readonly string[] NameSpacePrefix = new string[] { "AppService", "Application" };

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            if (context.Filters.Any(item => item is IAllowAnonymousFilter))
            {
                return;
            }

            if (!(context.ActionDescriptor is ControllerActionDescriptor))
            {
                return;
            }

            IEnumerable<Claim> claims = context.HttpContext.User.Identities.FirstOrDefault()?.Claims;
            context.HttpContext.Request.Headers.TryGetValue("Authorization", out StringValues token);
            Log.Information($"Recevied ------------ Authorization {token.ToString()}");

            if (claims == null || !claims.Any())
            {
                claims = context.HttpContext.AuthenticateAsync("Bearer").Result.Principal.Identities.FirstOrDefault()?.Claims;
            }

            if (claims == null || !claims.Any())
            {
                context.Result = new UnauthorizedResult();
                return;
            }

            ControllerActionDescriptor apiDescriptor = (ControllerActionDescriptor)context.ActionDescriptor;

            IEnumerable<HicapAuthorizeAttribute> actionHicapAuthorizeAttribute = apiDescriptor.MethodInfo.GetCustomAttributes(true)
                .OfType<HicapAuthorizeAttribute>();

            IEnumerable<HicapAuthorizeAttribute> controllerHicapAuthorizeAttribute = apiDescriptor.MethodInfo.DeclaringType.GetCustomAttributes(true)
                .OfType<HicapAuthorizeAttribute>();

            if (actionHicapAuthorizeAttribute.Any() || controllerHicapAuthorizeAttribute.Any())
            {
                string[] authObjects = claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.AuthorizationDecision))?.Value.Split(",");

                if (CheckSpeicaPermission(actionHicapAuthorizeAttribute, controllerHicapAuthorizeAttribute, authObjects))
                    return;

                if (CheckPermession(context, authObjects, apiDescriptor))
                    return;
            }

            return;
        }

        private static bool CheckSpeicaPermission(IEnumerable<HicapAuthorizeAttribute> actionHicapAuthorizeAttribute,
            IEnumerable<HicapAuthorizeAttribute> controllerHicapAuthorizeAttribute, string[] authObjects)
        {
            if (authObjects == null || authObjects.Length == 0)
                return false;

            if (actionHicapAuthorizeAttribute.Union(controllerHicapAuthorizeAttribute)
                .Any(x => authObjects.Any(y => y.Equals(x.AuthCode) && !string.IsNullOrEmpty(x.AuthCode))))
                return true;

            return false;
        }

        private static bool CheckPermession(AuthorizationFilterContext context, string[] authObjects,
            ControllerActionDescriptor apiDescriptor)
        {
            string authObjectName = AuthObjectHelper.ConvertActionNameToAuthObject(apiDescriptor.ActionName);

            if (authObjects == null || authObjects.Length == 0)
                context.Result = new UnauthorizedResult();

            string controllerAuthCode = ConvertAuthObject(apiDescriptor);

            if (authObjects.Any(x => x.Equals(SysConsts.SysName)) &&
                authObjects.Any(x => x.Equals(string.Join(".", controllerAuthCode, authObjectName).Trim('.'))))
                return true;
            else
                context.Result = new StatusCodeResult(403);

            return false;
        }

        private static string ConvertAuthObject(ControllerActionDescriptor apiDescriptor)
        {
            string tempstr = apiDescriptor.ControllerTypeInfo.FullName;

            NameSpacePrefix.ToList().ForEach(x => tempstr = tempstr.Replace(x, string.Empty));

            string controllerAuthCode = tempstr
                .Replace(SysConsts.SysName, string.Empty)
                .Replace("..", ".")
                .Trim('.');

            return controllerAuthCode;
        }
    }
}